home *** CD-ROM | disk | FTP | other *** search
/ Programming Languages Suite / ProgramD2.iso / Borland / Borland C++ V5.02 / WINSOCK.PAK / DLGSTRM.CPP < prev    next >
C/C++ Source or Header  |  1997-05-06  |  14KB  |  405 lines

  1. /*-----------------------------------------------------------------------*\
  2. | OWLSock Demo For Windows v1.0                                           |
  3. --------------------------------------------------------------------------|
  4. | Written By:  Paul Pedriana                                              |
  5. | Date:        May 7, 1995.                                               |
  6. | Copyright:   Copyright (c) 1995 by Paul Pedriana.  All Rights Reserved. |
  7. | UserID(s):   70541,3223                                                 |
  8. |              70541.3223@compuserve.com                                  |
  9. --------------------------------------------------------------------------|
  10. | This OWLSock demo is an application that demonstrates some features     |
  11. | of OWLSock.  It uses only asynchronous (non-blocking) Winsock calls,    |
  12. | and uses OWLSock socket 'external' notification rather than internal    |
  13. | notification.  External notification is the way most Winsock apps do    |
  14. | FD_XXX notifications; see the OWLSock docs for more info.               |
  15. --------------------------------------------------------------------------|
  16. | Notes on this module:                                                   |
  17. |    This module is a dialog box that implements a stream socket TCP      |
  18. | connection.  It either connects to a given port and address by itself,  |
  19. | or it can accept an already connected socket, e.g. a socket created by  |
  20. | an accept call.                                                         |
  21. |    Run another instance of this dialog on another computer and you can  |
  22. | connect the two computers together and send data back and forth.        |
  23. \*-----------------------------------------------------------------------*/
  24. #include <owl/pch.h>
  25. #if !defined(OWL_INPUTDIA_H)
  26. # include <owl/inputdia.h>
  27. #endif
  28. #if !defined(OWL_WINSOCK_H)
  29. # include <owl/winsock.h>
  30. #endif
  31. #include <stdlib.h>   //For atoi() call.
  32. #include "dlgstrm.h"
  33.  
  34. //********************************************************************************************
  35. DEFINE_RESPONSE_TABLE1(DlgSendStream, TDialog)
  36.   EV_CHILD_NOTIFY(IDC_BTN_SEND, BN_CLICKED, CmBtnSend),
  37.   EV_CHILD_NOTIFY(IDC_BTN_SEND_CLEAR, BN_CLICKED, CmBtnSendClear),
  38.   EV_CHILD_NOTIFY(IDC_BTN_RECEIVE_CLEAR, BN_CLICKED, CmBtnReceiveClear),
  39.   EV_CHILD_NOTIFY(IDC_BTN_CONNECT, BN_CLICKED, CmConnectDisconnect),
  40.   EV_MESSAGE(MSG_HOST_INFO_NOTIFY, DoHostNotification),
  41.   EV_MESSAGE(MSG_SOCKET_NOTIFY, DoSocketNotification),
  42. END_RESPONSE_TABLE;
  43.  
  44. DlgSendStream::DlgSendStream(TWindow* parent, TResId resId, TModule* module)
  45. :
  46.   TDialog(parent, resId, module),
  47.   myPresentState(nIdle),
  48.   sAddressToConnectTo(0, INADDR_NONE),
  49.   bDataSent(true)
  50. {
  51.   TINetSocketAddress ourSocketAddress; //same as INetSocketAddress(0, INADDR_ANY);
  52.  
  53.   myStreamSocket = new TStreamSocket(ourSocketAddress);
  54.   myStreamSocket->SetNotificationWindow(this); //redirect socket FD_XXX notifications to us.
  55. }
  56.  
  57.  
  58. DlgSendStream::~DlgSendStream()
  59. {
  60.   delete myStreamSocket; //It should usually be already deleted.
  61. }
  62.  
  63. void DlgSendStream::SetupWindow()
  64. {
  65.   editAddressSend    = new TEdit(this, IDC_EDIT_ADDRESS_SEND,   64);
  66.   editAddressReceive  = new TEdit(this, IDC_EDIT_ADDRESS_RECEIVE, 64);
  67.   editDataSend      = new TEdit(this, IDC_EDIT_SEND,        256);
  68.   editDataReceive    = new TEdit(this, IDC_EDIT_RECEIVE,      256);
  69.   editPort         = new TEdit(this, IDC_EDIT_PORT,         8);
  70.   staticStatus      = new TStatic(this, IDC_STATIC_STATUS,    32);
  71.   btnConnectDisconnect = new TButton(this, IDC_BTN_CONNECT);
  72.   TDialog::SetupWindow();
  73. }
  74.  
  75.  
  76. //
  77. // 'newConnectedSocket' is a valid and connected socket, usually connected with an Accept() call.
  78. // This function causes this object to
  79. // Returns true if able to take on the connected socket (with the given address).
  80. //  Usually will be unable to take on the newConnectedSocket if already connected with
  81. //  current socket.
  82. //
  83. short DlgSendStream::ConnectWithThis(TStreamSocket& newConnectedSocket)
  84. {
  85.   char  szPort[12];
  86.   int  nError;
  87.  
  88.   if (myPresentState != nIdle)
  89.     return 0; //Unable to comply, as we are busy.
  90.  
  91.   *myStreamSocket = newConnectedSocket; //StreamSocket::operator=()
  92.   myStreamSocket->SetSaveSocketOnDelete(FALSE); //Now we own the socket descriptor.
  93.   sAddressToConnectTo = newConnectedSocket.PeerSocketAddress;
  94.  
  95.   // The 'newConnectedSocket' may or may not be set up for Notification, so
  96.   //  we specifically set it here to make sure it is the way we want it:
  97.   //
  98.   myStreamSocket->SetNotificationWindow(this);
  99.   nError = myStreamSocket->StartRegularNotification();
  100.   if (nError == WINSOCK_ERROR) {
  101.     MessageBox(TSocketError(TWinSock::Dll()->WSAGetLastError()).AppendError("Error on StartRegularNotification() in ConnectWithThis()."), "Error", MB_OK);
  102.     GoToIdleState();
  103.     return 0;
  104.   }
  105.  
  106.   // Equivalent Winsock code:
  107.   //
  108.   nError = TWinSock::Dll()->WSAAsyncSelect(*myStreamSocket, *this, MSG_SOCKET_NOTIFY, FD_READ | FD_WRITE | FD_CLOSE | FD_CONNECT | FD_OOB);
  109.   if (nError) {
  110.     MessageBox(TSocketError(TWinSock::Dll()->WSAGetLastError()).AppendError("Error on WSASelect()"), "Error", MB_OK);
  111.     GoToIdleState();
  112.     return 0;
  113.   }
  114.  
  115.   wsprintf(szPort, "%d", TWinSock::Dll()->ntohs(sAddressToConnectTo.GetPort()));
  116.   editPort->SetWindowText(szPort); //disable editing.
  117.  
  118.   char far* szIPAddress;
  119.   szIPAddress = sAddressToConnectTo.ConvertAddress(sAddressToConnectTo.GetNetworkAddress());
  120.   editAddressSend->SetWindowText(szIPAddress);
  121.  
  122.   GoToConnectedState();
  123.   return 1;
  124. }
  125.  
  126. short DlgSendStream::ConnectWithThis(SOCKET& /*newSocket*/, TSocketAddress& /*sNewAddress*/)
  127. {
  128.   //This function presently not defined.
  129.   return 0;
  130. }
  131.  
  132.  
  133. TResult DlgSendStream::DoHostNotification(TParam1, TParam2 param2)
  134. {
  135.   int  nError = WSAGETASYNCERROR(param2);
  136.  
  137.   if (myPresentState != nWaitingForAddress)
  138.     return 1;
  139.  
  140.   if (nError) {
  141.     MessageBox(TSocketError(nError).AppendError("Error on looking up address."), "Error", MB_OK);
  142.     GoToIdleState();
  143.   }
  144.   else{
  145.     sAddressToConnectTo.SetNetworkAddress(myHostInfoManager.HostEntry->GetINetAddress());
  146.     GoToConnectingState();
  147.     Connect(); //Tries to connect to the given address.
  148.   }
  149.   return 1;
  150. }
  151.  
  152.  
  153. TResult DlgSendStream::DoSocketNotification(TParam1, TParam2 param2)
  154. {
  155.   int nEvent = (int)WSAGETSELECTEVENT(param2);
  156.   int nError = (int)WSAGETSELECTERROR(param2);
  157.  
  158.   if (nEvent == FD_WRITE && !bDataSent) {
  159.     if (nError)
  160.       MessageBox(TSocketError(nError).AppendError("Error on receipt of FD_WRITE."), "", MB_OK);
  161.     else
  162.       SendData();
  163.   }
  164.  
  165.   else if (nEvent == FD_READ) {
  166.     if (nError)
  167.       MessageBox(TSocketError(nError).AppendError("Error on receipt of FD_READ."), "", MB_OK);
  168.     else
  169.       ReadData();
  170.   }
  171.  
  172.   else if (nEvent == FD_CONNECT && myPresentState == nConnecting) {
  173.     if (nError) {
  174.       MessageBox(TSocketError(nError).AppendError("Error on receipt of FD_CONNECT."), "", MB_OK);
  175.       GoToIdleState();
  176.     }
  177.     else{
  178.       GoToConnectedState();
  179.     }
  180.   }
  181.  
  182.   else if (nEvent == FD_CLOSE) {
  183.     if (nError)
  184.       MessageBox(TSocketError(nError).AppendError("Error on receipt of FD_CLOSE."), "", MB_OK);
  185.     GoToIdleState();
  186.   }
  187.  
  188.   return 1;
  189. }
  190.  
  191. void DlgSendStream::Connect()
  192. {
  193.   //First create the socket.
  194.   int nError = myStreamSocket->CreateSocket(); //similar to 'socket()'
  195.   if (nError == WINSOCK_ERROR) { //could also say 'if (nError) {'
  196.     MessageBox(TSocketError(myStreamSocket->GetLastError()).AppendError("Error on CreateSocket()"), "Error", MB_OK);
  197.     GoToIdleState();
  198.     return;
  199.   }
  200.  
  201.   //The StartRegularNotification() function converts socket into non-blocking socket.
  202.   myStreamSocket->StartRegularNotification();
  203.  
  204.   //Bind is technically not really needed, as a connect() will bind for you:
  205.   nError = myStreamSocket->BindSocket();
  206.   if (nError == WINSOCK_ERROR) { //could also say 'if (nError) {'
  207.     MessageBox(TSocketError(myStreamSocket->GetLastError()).AppendError("Error on BindSocket()"), "Error", MB_OK);
  208.     GoToIdleState();
  209.     return;
  210.   }
  211.  
  212.   nError = myStreamSocket->Connect(sAddressToConnectTo);
  213.   if (nError == WINSOCK_ERROR) {
  214.     if (myStreamSocket->GetLastError() != WSAEWOULDBLOCK) {
  215.       MessageBox(TSocketError(myStreamSocket->GetLastError()).AppendError("Error on Connect()"), "Error", MB_OK);
  216.       GoToIdleState();
  217.       return;
  218.     }
  219.     //Else, we will soon get asynchronously notified of a completed connection with FD_CONNECT.
  220.   }
  221. }
  222.  
  223.  
  224.  
  225. void DlgSendStream::ReadData() {
  226.   char szData[256];
  227.   int  nLength=255;
  228.   int  nEditLength;
  229.   int  nError;
  230.  
  231.   nError = myStreamSocket->Read(szData, nLength);
  232.   if (nError == WINSOCK_ERROR) {
  233.     MessageBox(TSocketError(myStreamSocket->GetLastError()).AppendError("Error trying to read data."), "", MB_OK);
  234.     return;
  235.   }
  236.   szData[nLength]=0; //Null-terminate the data to make it a C-string.
  237.  
  238.   nEditLength = editDataReceive->GetWindowTextLength();
  239.   editDataReceive->SetSelection(nEditLength, nEditLength); //Move cursor to end of text.
  240.   editDataReceive->Insert(szData); //Append the new text.
  241. }
  242.  
  243.  
  244.  
  245. void DlgSendStream::SendData() {
  246.   int nError;
  247.   int nCharsToSend;
  248.  
  249.   nCharsToSend = lstrlen(szDataToSend);
  250.   nError = myStreamSocket->Write(szDataToSend, nCharsToSend);
  251.   if (nError == WINSOCK_ERROR) {
  252.     if (myStreamSocket->GetLastError() != WSAEWOULDBLOCK)
  253.       bDataSent = TRUE;
  254.       MessageBox(TSocketError(myStreamSocket->GetLastError()).AppendError("Error trying to write data."), "", MB_OK);
  255.     return;
  256.   }
  257.   bDataSent = TRUE;
  258.   MessageBox("Send succeeded", "", MB_OK);
  259. }
  260.  
  261.  
  262.  
  263. void DlgSendStream::CmConnectDisconnect() {
  264.   short  bAddressIsDottedDecimal;
  265.   char   szAddressInput[256];
  266.   u_short nPortEnteredByUser;
  267.   HANDLE  hHostRequest;
  268.   int    nError;
  269.  
  270.   if (myPresentState == nIdle) {
  271.     GoToWaitingForAddressState();
  272.  
  273.     //Get port entered by user.
  274.     editPort->GetWindowText(szAddressInput, 255);
  275.     nPortEnteredByUser = TWinSock::Dll()->htons((u_short)atoi(szAddressInput));  //need an 'atous()' function.
  276.     sAddressToConnectTo.SetPort(nPortEnteredByUser);
  277.  
  278.     //Get actual address entered by user.
  279.     editAddressSend->GetWindowText(szAddressInput, 255);
  280.     bAddressIsDottedDecimal = sAddressToConnectTo.IsAddressDottedDecimal(szAddressInput);
  281.     if (bAddressIsDottedDecimal) {
  282.       sAddressToConnectTo.SetNetworkAddress(sAddressToConnectTo.ConvertAddress(szAddressInput));
  283.       //We can immediately move on to the 'nConnecting' state.
  284.       GoToConnectingState();
  285.       Connect();
  286.     }
  287.     else{
  288.       nError = myHostInfoManager.GetHostInfoAsync(*this, hHostRequest, szAddressInput);
  289.       if (nError == WINSOCK_ERROR) {
  290.         MessageBox(TSocketError(myHostInfoManager.GetLastError()).AppendError("Unable to convert the host name to an address."), "Error", MB_OK);
  291.         GoToIdleState();
  292.       }
  293.     }
  294.   }
  295.  
  296.   else if (myPresentState == nWaitingForAddress) {
  297.     //cancel the lookup and go back to idle
  298.     myHostInfoManager.CancelHostRequest();
  299.     GoToIdleState();
  300.   }
  301.  
  302.   else if (myPresentState == nConnecting) {
  303.     //cancel the connection attempt and go back to idle.
  304.       //I don't know how to cancel a connection attempt...
  305.       //MessageBox("Busy connecting", "", MB_OK);
  306.     //Maybe this will work (?):
  307.     int nError;
  308.     nError = TWinSock::Dll()->WSACancelBlockingCall();
  309.     if (nError == SOCKET_ERROR) {
  310.       nError = TWinSock::Dll()->WSAGetLastError();
  311.       MessageBox(TSocketError(nError).AppendError("Can't cancel connection attempt."), "", MB_OK);
  312.     }
  313.     else
  314.       GoToIdleState();
  315.   }
  316.  
  317.   else if (myPresentState == nConnected) {
  318.     //simply disconnect and return to idle.
  319.     myStreamSocket->ShutDownSocket();
  320.     GoToIdleState();
  321.   }
  322. }
  323.  
  324.  
  325. void DlgSendStream::CmBtnSend() {
  326.   if (myPresentState != nConnected) {
  327.     MessageBox("This socket is not connected", "", MB_OK);
  328.     return;
  329.   }
  330.   editDataSend->GetWindowText(szDataToSend, 256);
  331.   if (szDataToSend[0]) {
  332.     bDataSent = FALSE;
  333.     SendData();
  334.   }
  335.   else
  336.     MessageBox("There is nothing to send.", "", MB_OK);
  337. }
  338.  
  339.  
  340. void DlgSendStream::CmBtnSendClear() {
  341.   editDataSend->Clear(); //Clear the text.
  342. }
  343.  
  344.  
  345. void DlgSendStream::CmBtnReceiveClear() {
  346.   editDataReceive->Clear(); //Clear the text.
  347. }
  348.  
  349.  
  350. void DlgSendStream::CmOk() {
  351.   if (myPresentState != nIdle) {
  352.     CmConnectDisconnect(); //Disconnect/Cancel the operation and return all to idle.
  353.   }
  354.   TDialog::CmOk();
  355. }
  356.  
  357.  
  358. void DlgSendStream::GoToIdleState() {
  359.   bDataSent = 1;
  360.   myPresentState = nIdle;
  361.   staticStatus->SetWindowText("Status: Idle");
  362.   btnConnectDisconnect->SetWindowText("Connect");
  363.   editPort->SetReadOnly(FALSE); //enable editing.
  364.   editAddressSend->SetReadOnly(FALSE);
  365.   editAddressReceive->Clear();
  366.  
  367.   //We close the socket here if it was open already.
  368.   myStreamSocket->CloseSocket();
  369. }
  370.  
  371.  
  372. void DlgSendStream::GoToWaitingForAddressState() {
  373.   myPresentState = nWaitingForAddress;
  374.   staticStatus->SetWindowText("Status: Looking Up Address...");
  375.   btnConnectDisconnect->SetWindowText("Cancel");
  376.   editPort->SetReadOnly(TRUE); //disable editing.
  377.   editAddressSend->SetReadOnly(TRUE);
  378. }
  379.  
  380. void DlgSendStream::GoToConnectingState() {
  381.   myPresentState = nConnecting;
  382.   staticStatus->SetWindowText("Status: Connecting...");
  383.  
  384.   //Not necessary, because previous state already set this:
  385.   //btnConnectDisconnect->SetWindowText("Cancel");
  386.   //editPort->SetReadOnly(TRUE); //disable editing.
  387.   //editAddressSend->SetReadOnly(TRUE);
  388. }
  389.  
  390.  
  391. void DlgSendStream::GoToConnectedState() {
  392.   char szIPAddress[20];
  393.  
  394.   myPresentState = nConnected;
  395.   staticStatus->SetWindowText("Status: Connected");
  396.   btnConnectDisconnect->SetWindowText("Disconnect");
  397.  
  398.   //Actually necessary, because we could go directly from nIdle to
  399.   //  nConnected with the ConnectWithThis() function:
  400.   editPort->SetReadOnly(TRUE); //disable editing.
  401.   editAddressSend->SetReadOnly(TRUE);
  402.   editAddressSend->GetWindowText(szIPAddress, 20);
  403.   editAddressReceive->SetWindowText(szIPAddress);
  404. }
  405.